/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.lucene.util;


import java.util.Arrays;

/**
 * Estimates how {@link RamUsageEstimator} estimates physical memory consumption
 * of Java objects.
 */
public class StressRamUsageEstimator extends LuceneTestCase {
    volatile Object guard;

    // This shows an easy stack overflow because we're counting recursively.
    public void testLargeSetOfByteArrays() {

        System.gc();
        long before = Runtime.getRuntime().totalMemory();
        Object[] all = new Object[1000000];
        for (int i = 0; i < all.length; i++) {
            all[i] = new byte[random().nextInt(3)];
        }
        System.gc();
        long after = Runtime.getRuntime().totalMemory();
        System.out.println("mx:  " + RamUsageEstimator.humanReadableUnits(after - before));
        System.out.println("rue: " + RamUsageEstimator.humanReadableUnits(shallowSizeOf(all)));

        guard = all;
    }

    private long shallowSizeOf(Object[] all) {
        long s = RamUsageEstimator.shallowSizeOf(all);
        for (Object o : all) {
            s += RamUsageEstimator.shallowSizeOf(o);
        }
        return s;
    }

    private long shallowSizeOf(Object[][] all) {
        long s = RamUsageEstimator.shallowSizeOf(all);
        for (Object[] o : all) {
            s += RamUsageEstimator.shallowSizeOf(o);
            for (Object o2 : o) {
                s += RamUsageEstimator.shallowSizeOf(o2);
            }
        }
        return s;
    }

    public void testSimpleByteArrays() {
        Object[][] all = new Object[0][];
        try {
            while (true) {
                // Check the current memory consumption and provide the estimate.
                System.gc();
                long estimated = shallowSizeOf(all);
                if (estimated > 50 * RamUsageEstimator.ONE_MB) {
                    break;
                }

                // Make another batch of objects.
                Object[] seg = new Object[10000];
                all = Arrays.copyOf(all, all.length + 1);
                all[all.length - 1] = seg;
                for (int i = 0; i < seg.length; i++) {
                    seg[i] = new byte[random().nextInt(7)];
                }
            }
        } catch (OutOfMemoryError e) {
            // Release and quit.
        }
    }

    static class Entry {
        Object o;
        Entry  next;

        public Entry createNext(Object o) {
            Entry e = new Entry();
            e.o = o;
            e.next = next;
            this.next = e;
            return e;
        }
    }
}
